﻿using System;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows;
using System.Windows.Interactivity; 

//===================================================================================
// This is adopted from Prism, from the namespace 
// namespace Microsoft.Practices.Composite.Presentation.Commands
//===================================================================================
namespace UltimateNavigationFramework.Commands
{
    /// <summary>
    /// Base behaviour to handle connecting a <see cref="Control"/> to a Command.
    /// </summary>
    /// <typeparam name="T">The target object must derive from Control</typeparam>
    /// <remarks>
    /// CommandBehaviourBase can be used to provide new behaviours similar to <see cref="ButtonBaseClickCommandBehaviour"/>.
    /// </remarks>
    public class CommandBehaviourBase<T> : Behavior<T>
        where T : UIElement
    {
        private ICommand _command;
        private object _commandParameter;
        private readonly WeakReference _targetObject;


        /// <summary>
        /// Constructor specifying the target object.
        /// </summary>
        /// <param name="targetObject">The target object the behaviour is attached to.</param>
        public CommandBehaviourBase(T targetObject)
        {
            this._targetObject = new WeakReference(targetObject);
        }

        #region Properties

        /// <summary>
        /// Corresponding command to be execute and monitored for <see cref="ICommand.CanExecuteChanged"/>
        /// </summary>
        public ICommand Command
        {
            get { return _command; }
            set
            {
                if (this._command != null)
                {
                    this._command.CanExecuteChanged -= this.CommandCanExecuteChanged;
                }

                this._command = value;
                if (this._command != null)
                {
                    this._command.CanExecuteChanged += this.CommandCanExecuteChanged;
                    UpdateEnabledState();
                }
            }
        }

        /// <summary>
        /// The parameter to supply the command during execution
        /// </summary>
        public object CommandParameter
        {
            get { return this._commandParameter; }
            set
            {
                if (this._commandParameter != value)
                {
                    this._commandParameter = value;
                    this.UpdateEnabledState();
                }
            }
        }

        /// <summary>
        /// Object to which this behaviour is attached.
        /// </summary>
        protected T TargetObject
        {
            get
            {
                return _targetObject.Target as T;
            }
        }

        /// <summary>
        /// Allows disabling Controls types that cannot execute the command.
        /// </summary>
        protected virtual bool AutoDisableControl { get; set; }

        /// <summary>
        /// Attaches to the target <see cref="FrameworkElement"/> and sets up the command.
        /// </summary>
        protected override void OnAttached()
        {
            base.OnAttached();

            this.UpdateEnabledState();
        }
        #endregion

        #region Internal

        /// <summary>
        /// Updates the target object's IsEnabled property based on the commands ability to execute.
        /// </summary>
        protected virtual void UpdateEnabledState()
        {
            if (TargetObject == null)
            {
                this.Command = null;
                this.CommandParameter = null;
            }
            else if (this.Command != null)
            {
                // we can only disable if it is of type control
                if (AutoDisableControl && typeof(Control).IsAssignableFrom(TargetObject.GetType()))
                {
                    var control = TargetObject as Control;
                    if (control != null) control.IsEnabled = this.Command.CanExecute(this.CommandParameter);
                }                
            }
        }

        private void CommandCanExecuteChanged(object sender, EventArgs e)
        {
            this.UpdateEnabledState();
        }

        /// <summary>
        /// Executes the command, if it's set, providing the <see cref="CommandParameter"/>
        /// </summary>
        protected virtual void ExecuteCommand()
        {
            if (this.Command != null)
            {
                this.Command.Execute(this.CommandParameter);
            }
        }
        #endregion
    }
}

